/*
 * 物理内存分配器实现
 * 基于空闲页链表的简单分配算法
 * 参考xv6的实现
 */

#include "kalloc.h"
#include "../fs/types.h"
#include "riscv.h"
#include "memlayout.h"
#include "../fs/printf.h"

// 空闲页链表节点
struct run {
    struct run *next;
};

// 内存分配器状态
struct {
    struct run *freelist;  // 空闲页链表头
    uint64 free_pages;     // 空闲页数量
    uint64 total_pages;    // 总页数量
} kmem;

// 内存设置函数destination
static void memset(void *dst, int c, uint64 n)
{
    char *cdst = (char*)dst;
    for (uint64 i = 0; i < n; i++) {
        cdst[i] = c;
    }
}

// 内核结束地址（由链接器脚本定义）
extern char end[];

/*
 * 初始化物理内存分配器
 * 将内核结束地址到PHYSTOP之间的内存加入空闲链表
 */
void pmm_init(void)
{
    kmem.freelist = 0;
    kmem.free_pages = 0;
    kmem.total_pages = 0;
    
    printf("Physical memory allocator initializing...\n");
    printf("Kernel end: 0x%lx\n", (uint64)end);
    printf("Physical memory top: 0x%lx\n", PHYSTOP);
    
    printf("About to call freerange...\n");
    // 将可用物理内存范围加入空闲链表
    freerange(end, (void*)PHYSTOP);
    printf("freerange completed.\n");
    
    printf("Physical memory allocator initialized.\n");
    printf("Total pages: %ld, Free pages: %ld\n", kmem.total_pages, kmem.free_pages);
}

/*
 * 将指定范围的物理内存加入空闲链表
 * pa_start: 起始物理地址
 * pa_end: 结束物理地址
 */
void freerange(void *pa_start, void *pa_end)
{
    char *p;
    
    printf("freerange: start=0x%lx, end=0x%lx\n", (uint64)pa_start, (uint64)pa_end);
    
    // 将起始地址向上对齐到页边界
    p = (char*)PGROUNDUP((uint64)pa_start);
    printf("freerange: aligned start=0x%lx\n", (uint64)p);
    
    // 逐页释放到空闲链表
    int count = 0;
    for (; p + PGSIZE <= (char*)pa_end; p += PGSIZE) {
        if (count % 1000 == 0) {
            printf("freerange: processing page %d at 0x%lx\n", count, (uint64)p);
        }
        free_page(p);
        kmem.total_pages++;
        count++;
    }
    printf("freerange: processed %d pages\n", count);
}

/*
 * 释放一个物理页到空闲链表
 * pa: 要释放的物理页地址（必须页对齐）
 */
void free_page(void *pa)
{
    struct run *r;
    
    // 地址页对齐
    if (((uint64)pa % PGSIZE) != 0) {
        printf("kfree: address not page aligned: 0x%lx\n", (uint64)pa);
        return;
    }
    
    // 地址在有效范围内
    if ((char*)pa < end || (uint64)pa >= PHYSTOP) {
        printf("kfree: address out of range: 0x%lx\n", (uint64)pa);
        return;
    }
    
    // 暂时跳过页面清零，可能会出现数据泄露等问题
    // 将页面加入空闲链表头部
    r = (struct run*)pa;
    r->next = kmem.freelist;
    kmem.freelist = r;
    kmem.free_pages++;
}

/*
 * 分配一个物理页
 * 返回: 分配的页面地址，失败时返回0
 */
void* alloc_page(void)
{
    struct run *r;
    
    // 从空闲链表头部取出一页
    r = kmem.freelist;
    if (r) {
        kmem.freelist = r->next;
        kmem.free_pages--;
        
        // 用垃圾数据填充页面，帮助发现未初始化使用问题
        memset((char*)r, 5, PGSIZE);
    }
    
    return (void*)r;
}

/*
 * 获取空闲页数量
 */
uint64 get_free_pages(void)
{
    return kmem.free_pages;
}

/*
 * 获取总页数量
 */
uint64 get_total_pages(void)
{
    return kmem.total_pages;
}

/*
 * 打印内存分配器状态,用于调试
 */
void print_kmem_stats(void)
{
    printf("Memory allocator statistics:\n");
    printf("  Total pages: %ld (%ld MB)\n", 
           kmem.total_pages, (kmem.total_pages * PGSIZE) / (1024 * 1024));
    printf("  Free pages: %ld (%ld MB)\n", 
           kmem.free_pages, (kmem.free_pages * PGSIZE) / (1024 * 1024));
    printf("  Used pages: %ld (%ld MB)\n", 
           kmem.total_pages - kmem.free_pages, 
           ((kmem.total_pages - kmem.free_pages) * PGSIZE) / (1024 * 1024));
    printf("  Page size: %d bytes\n", PGSIZE);
}